#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <GL/glut.h>

#include "terreno.h"
#include "image.h"
#include "objeto3d.h"

/* tamanho da tela */
int s_width = 960;
int s_height = 720;

/*camera atual */
float angulo = 0;

/* Centro de massa */
struct Vetor3 centro;

/* texturas */
GLuint textura_grama;
GLuint textura_marmore;

/* Posicao da luz */
GLfloat light_position[] = { 25.0, 10.0, 1.0, 0.0 };

#define PI 3.1415926535897932384626433832795f

#define MOD_TEMPLO	1
#define MOD_TERRENO	2
#define MOD_SOMBRA	3

#define ARQUIVO_TEMPLO "data/parthenon.txt"
#define ARQUIVO_GRAMA "data/grass.bmp"
#define ARQUIVO_MARMORE "data/marble.bmp"

#define DIST_CAMERA 20.0f

#define TERRENO_W 0.7f
#define TERRENO_D 0.7f

/****************************** seta camera N *********************************/
void setCamera(float angulo)
{
	glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(centro.x+(DIST_CAMERA*cos(angulo)), centro.y, centro.z+(DIST_CAMERA*sin(angulo)),
              centro.x, centro.y, centro.z,
              0, 1, 0);
}

/******************************* init *****************************************/
void init(void)
{
	glClearColor(0.8,0.9,1.0,0.0);	// set background color

    glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
    glClearDepth(1.0f);									// Depth Buffer Setup
    glClearStencil(0);									// Stencil Buffer Setup
    glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
    glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

    /* ILUMINACAO */
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 100.0 };

    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glEnable(GL_LIGHTING);

    /* Culling */
    glCullFace(GL_BACK);								// Set Culling Face To Back Face
    glEnable(GL_CULL_FACE);								// Enable Culling

    /* Textura */
    glEnable(GL_TEXTURE_2D);
}

/******************************* display **************************************/
void display(void)
{ 
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);

    glLoadIdentity();
    setCamera(angulo);

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    glPushMatrix();
    glTranslatef(-(width/2.)*TERRENO_W, -.7, -(height/2.)*TERRENO_D);
	glCallList(MOD_TERRENO);
    glPopMatrix();

	glCallList(MOD_TEMPLO);

	glCallList(MOD_SOMBRA);

    glFlush();
	glutSwapBuffers();
}

/******************************* timer *****************************************/
void timer(int value)
{
    angulo -= .03f;

    glutPostRedisplay();
	glutTimerFunc(50, timer, value+1);
}

/******************************* reshape **************************************/

void reshape(int w, int h)
{
	glViewport(0, 0, w, h);
	s_width = w;
	s_height = h;

    glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
    glLoadIdentity();									// Reset The Projection Matrix

	gluPerspective( 45.0, s_width/s_height, 0.1f, 200.0);

    glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
    glLoadIdentity();									// Reset The Modelview Matrix
}

/******************************* loadTextura ***********************************/
int loadTextura(GLuint *textura, char *arquivo)
{
    Image imagem;
    imagem.sizeX = 0;
    imagem.sizeY = 0;
    imagem.data = NULL;
    
    if (ImageLoad(&imagem, arquivo))
    {
        /* cria textura */
        glGenTextures(1, textura);

        /* Typical Texture Generation Using Data From The Bitmap */
        glBindTexture(GL_TEXTURE_2D, *textura);

        // Generate The Texture
		glTexImage2D(GL_TEXTURE_2D, 0, 3, imagem.sizeX, imagem.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, imagem.data);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// Linear Filtering
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Filtering

        return 1;
    }

    /* Libera memoria */
    if (imagem.data)
        free(imagem.data);

    return 0;
}

/******************************* main *****************************************/
int main(int argc, char** argv)
{
    FILE *arquivo;

    /* Inicializa GLUT */
	glutInit(&argc, argv);							// initialize the toolkit
    glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL | GLUT_DEPTH );
	glutInitWindowSize(s_width,s_height);				// *** tamanho da Janela ***
	glutInitWindowPosition(100, 150);				// set window position on screen
	glutCreateWindow("Templo");					// open the screen window
	glutDisplayFunc(display);						// register redraw function
	glutReshapeFunc(reshape);
	glutTimerFunc(50, timer, 0);
	init();

    /* inicializa gerador aleatorio */
	srand(time(NULL));

	/* Contagem de pontos e soma */
	int total = 0;

    /* Carrega textura do terreno */
    if (!loadTextura(&textura_grama, ARQUIVO_GRAMA))
    {
        fprintf(stderr, "%s: Nao pode carregar textura do arquivo: %s.\n", argv[0], ARQUIVO_GRAMA);
        exit(1);
    }

	/* Cria lista para o modelo do terreno */
	glNewList(MOD_TERRENO, GL_COMPILE);

    int x, z;
    float wx, wy;
    float vy, vz;

    /* Seleciona textura */
    glBindTexture(GL_TEXTURE_2D, textura_grama);

    /* Cor */    
    glColor4f(0.8f, 1.0f, 0.9f, 1.0f);

    for (x = 0; x < width; x++)
    {
        glBegin(GL_QUAD_STRIP);
        for (z = 0; z < height; z++)
        {
            wx = ((x+1)*TERRENO_W) - (x*TERRENO_W);
            wy = (header_data[((x+1)*height)+z] - header_data[(x*height)+z])/10.;
//            wz = 0;

            if (z > 0)
            {
//                vx = 0;
                vy = (header_data[((x+1)*height)+z] - header_data[((x+1)*height)+(z-1)])/10.;
                vz = (z*TERRENO_D)-((z-1)*TERRENO_D);

                glNormal3f(-vz*wy, vz*wx, -vy*wx);
            }
            glTexCoord2f((x+1)/(float)width, z/(float)height);
            glVertex3f((x+1)*TERRENO_W, header_data[((x+1)*height)+z]/10., z*TERRENO_D);

            if (z > 0)
            {
//                vx = 0;
                vy = (header_data[(x*height)+z] - header_data[(x*height)+(z-1)])/10.;
                vz = (z*TERRENO_D)-((z-1)*TERRENO_D);

                glNormal3f(-vz*wy, vz*wx, -vy*wx);
            }
            glTexCoord2f(x/(float)width, z/(float)height);
            glVertex3f(x*TERRENO_W, header_data[(x*height)+z]/10., z*TERRENO_D);
        }
        glEnd();
    }
	glEndList();

	/* Le objeto do arquivo */
	if ((arquivo = fopen(ARQUIVO_TEMPLO, "r")) == NULL)
	{
		fprintf(stderr, "%s: Erro ao ler %s.\n", argv[0], ARQUIVO_TEMPLO);
		exit(1);
	}
	
	/* Le objeto templo do arquivo */
	struct Objeto3D templo3D = lerObjeto3D(arquivo);
	fclose(arquivo);

    /* Carrega textura do templo */
    if (!loadTextura(&textura_marmore, ARQUIVO_MARMORE))
    {
        fprintf(stderr, "%s: Nao pode carregar textura do arquivo: %s.\n", argv[0], ARQUIVO_MARMORE);
        exit(1);
    }

	/* Cria lista para o modelo do templo */
	glNewList(MOD_TEMPLO, GL_COMPILE);

    /* Seleciona textura */
    glBindTexture(GL_TEXTURE_2D, textura_marmore);

    /* Cor */    
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

    /* Desenha objeto */
    centro = desenhaObjeto3D(templo3D);

    glEndList();

	/* Cria lista para o modelo da sombra do templo */
	glNewList(MOD_SOMBRA, GL_COMPILE);
    /* Desenha sombra do templo */
    desenhaSombra(templo3D, light_position);

    glEndList();

    /* Libera memoria */
    killObjeto3D(templo3D);

	/* Seta camera */
    setCamera(angulo);

	glutMainLoop();			// go into a perpetual loop
}

